More on Messages and Threads
Back to Introduction to Messages
Back to Win95 ASM Page
Threads and windows
Whenever a window is created, it is "bound" to the current thread. That
thread is the only thread that can execute the window procedure for that
particular window. Consequently, it is the only thread that can process
messages for that window. In a sense, the thread "owns" the window.
However, another window (of the same class) can
be created in another thread, allowing the two windows to process messages
concurrently. Because more than one thread can be executing a given window
procedure, the following discussions talk about functions being "called
by the thread" instead.
Preempting or nonpreempting?
While the thread system is preemptive, the message system is not. A thread
is preempted solely to let other threads run. A thread is never preempted
to change its point of execution. Thus a thread, in general, won't respond
immediately to new messages. [But see same-thread
sends.]
That's because message systems have three basic
components: senders, queues, and receivers. A sender routine will
put a message in some designated queue, and a receiver routine must pull
it out before a message handler can process it. If a receiver routine isn't
called by a thread, the thread cannot handle any messages sitting in the
queue. Windows provides two functions for receiving messages: GetMessage
and PeekMessage.
Threads and message queues
"Posted" messages are delivered to threads. That means each thread that
expects messages will have its own message queue. Because each window is
associated with exactly one thread, PostMessage can determine which thread
to post to (without needing a thread argument).
"Sent" messages are, on the surface, delivered to
windows. Because threads do the work of handling received messages, "sent"
messages must actually be delivered to threads. Except for same-thread
messages [see Same-thread sends], such messages
must be placed in a thread message queue -- one that is associated with
the destination window. Whether or not this queue is the same as the "PostMessage"
queue is unimportant. It's only important for the system to know if a message
is from a "SendMessage" function or a "PostMessage" function.
Thread blocking and SendMessage
When sending a message to a window, the sending function may need to wait
for a reply. While it is waiting, the thread which called
the function is said to be blocked. This situation prevents
the thread from handling any messages for other windows it "owns". Thus
window contents don't get updated, and when a window is uncovered, it doesn't
get redrawn.
When SendMessage sends a message to a window in
another thread, it waits for a reply which is issued when the receiving
thread calls ReplyMessage. An explicit reply is usually not necessary because
when a window procedure exits, ReplyMessage is automatically called. An
explicit ReplyMessage will be necessary to avoid deadlock
if there is chain of unfinished SendMessage calls that involves more than
one thread in a circular fashion. [See Deadlock
Theory.]
For example:
The SendMessage chain
(window A, thread 1) -->
(B, thread 2) --> ... --> (C, thread n) --> (D, [back to] thread 1)
will deadlock when C (in thread n) sends a message
to D, if none of the window procedures have called ReplyMessage. If any
of the invoked window procedures (other than D) call ReplyMessage before
calling SendMessage, the chain is broken.
Same-thread sends
SendMessage, a thread blocking function, is used quite often to send a
message to a window in the same thread. Under normal rules, this
function would put the message on a queue and then force the thread to
wait for itself to reply. Since it's waiting, it can't reply--deadlock.
In this special case, a "same-thread" send, Windows will directly call
the proper window procedure as if it were a subroutine. In effect, this
is thread preemption for the purpose of responding to new messages.
Because it doesn't require the message to be pulled
out of a queue, this behavior allows messages to be processed without a
message loop. For example, CreateWindowEx can invoke your special WM_CREATE
code, and draw the window frame before your app enters its message loop.
Also, any window (created in the same thread) can be updated immediately
by sending it the appropriate message.
But be aware that this immediate response occurs
only when a message is sent (with SendMessage) to a window in the
same thread.
Forms of message transmission
SendMessage is a thread blocking function. It always waits for a reply.
SendMessageTimeout blocks like SendMessage, but
it unblocks if the receiver takes too long to reply. You specify the timeout
interval.
SendNotifyMessage is a nonblocking function. It
doesn't wait for a reply, unless the destination window is in the same
thread.
SendMessageCallback is another nonblocking function.
It also doesn't wait for a reply, unless the destination window is in the
same thread. You specify a callback routine which will be executed when
the receiving thread replies.
PostMessage is completely nonblocking. It never
waits for a reply. You must target a window. The window determines which
thread will receive the message.
PostThreadMessage is nonblocking and can't target
a window. You must target a thread.
PostQuitMessage is nonblocking and posts a WM_QUIT
message to the current thread.